package cn.wygandwdn.core.codec;

import cn.wygandwdn.common.constant.RpcConstants;
import cn.wygandwdn.common.enumeration.ErrorType;
import cn.wygandwdn.common.exception.RpcException;
import cn.wygandwdn.common.message.Message;
import cn.wygandwdn.core.config.Config;
import cn.wygandwdn.core.serializer.SerializerUtil;
import cn.wygandwdn.core.serializer.WtSerializer;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageCodec;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;

/**
 * @description:    WT-RPC自定义协议编解码器
 * 0             1             2             3             4             5             6             7             8
 * +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+
 * |                        magic_number                   |   version   |serializeType| messageType |     fill    |
 * +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+
 * |                                                                                                               |
 * |                                                  body                                                         |
 * |                                                                                                               |
 * +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+
 * 4byte magic_number(魔数)   1byte version(版本号)  1byte serializeType(序列化器类型) 1byte messageType(消息类型)
 * 1byte fill(对齐填充) body实际数据
 *
 * @author: WYG
 * @time: 2021/10/25 10:25
 */

@ChannelHandler.Sharable
public class MessageCodec extends MessageToMessageCodec<ByteBuf, Message> {

    private static final Logger log = LoggerFactory.getLogger(MessageCodec.class);


    @Override
    protected void encode(ChannelHandlerContext ctx, Message msg, List<Object> out) throws Exception {
        ByteBuf buffer = ctx.alloc().buffer();
        // 1、4字节的魔数
        buffer.writeBytes(RpcConstants.MAGIC_NUMBERS);
        // 2、1字节版本号
        buffer.writeByte(RpcConstants.VERSION);
        // 3、1字节的序列化方式
        buffer.writeByte(Config.getSerializeType());
        // 4、1字节的消息类型
        buffer.writeByte(msg.getMessageType());
        // 5、对其填充
        buffer.writeByte(0xff);
        WtSerializer serializer = SerializerUtil.getSerializer(Config.getSerializeType());
        byte[] bytes = serializer.serializeToByte(msg);
        // 6、数据长度
        buffer.writeInt(bytes.length);
        // 7、实际内容
        buffer.writeBytes(bytes);

        out.add(buffer);
    }

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {
        // 1、读取魔数并校验
        byte[] magic = new byte[RpcConstants.MAGIC_NUMBERS.length];
        msg.readBytes(magic);
        checkMagic(magic);

        // 2、读取RPC版本号并校验
        byte version = msg.readByte();
        checkVersion(version);
        // 3、读取客户端序列化方式
        int serializeType = msg.readByte();
        WtSerializer serializer = SerializerUtil.getSerializer(serializeType);
        if (serializer == null) {
            log.error("无法识别的序列化器: {}", serializeType);
            throw new RpcException(ErrorType.UNKNOWN_SERIALIZER);
        }
        // 4、读取消息类型
        int messageType = msg.readByte();
        Class<? extends Message> messageClass = Message.getMessageClass(messageType);
        if (messageClass == null) {
            log.error("无法识别的消息类型: {}", messageType);
            throw new RpcException(ErrorType.UNKNOWN_MESSAGE_CLASS);
        }
        // 5、读取对齐填充
        msg.readByte();
        // 6、读取数据长度
        int length = msg.readInt();
        // 7、读取数据
        byte[] content = new byte[length];
        msg.readBytes(content);
        // 8、解码
        Message message = serializer.deserializeFromByte(content, messageClass);
        if (message == null) {
            log.error("反序列化失败");
            throw new RpcException(ErrorType.FAILED_SERIALIZE);
        }
        out.add(message);
    }


    private void checkMagic(byte[] magic) {
        for (int i = 0; i < magic.length; i++) {
            if (magic[i] != RpcConstants.MAGIC_NUMBERS[i]) {
                log.error("不支持的数据包: {}", magic);
                throw new RpcException(ErrorType.NOT_SUPPORT);
            }
        }
    }


    private void checkVersion(byte version) {
        if (version != RpcConstants.VERSION) {
            log.error("不支持的版本: {}", version);
            throw new RpcException(ErrorType.NOT_SUPPORT_VERSION);
        }
    }
}
